#include <devices.h>
#include <io.h>
#include <string.h>
#include <acces_modes.h>
#include <sched.h>
#include <mm.h>
#include <zeosfat.h>
#include <errno.h>

void init_devices()
{
  int i;
  /* Inicialitzem la TFA */
  for (i = 0; i < TFA_SIZE;++i)
    TFA[i].nref = 0;
    
  /* Inicialitzem els dispositius logics */
  for (i = 0; i < NR_LOGIC_DEVICES;++i)
    devices[i].status=FREE;
    
  /* Inicialitzem la pantalla */
  strcopy(devices[0].name,"DISPLAY");
  devices[0].status = USED;
  devices[0].nref = 1;
  devices[0].acces_mode = O_WRONLY;
  //devices[0].ops.sys_open_dep = NULL;
  devices[0].ops.sys_write_dep = sys_write_console;
  devices[0].ops.sys_read_dep = NULL;
  devices[0].ops.sys_unlink_dep = NULL;
  
  /* Inicialitzem el teclat */
  strcopy(devices[1].name,"KEYBOARD");
  devices[1].status = USED;
  devices[1].nref = 1;
  devices[1].acces_mode = O_RDONLY;
  //devices[1].ops.sys_open_dep = NULL;
  devices[1].ops.sys_write_dep = NULL;
  devices[1].ops.sys_read_dep = sys_read_keyboard;
  devices[1].ops.sys_unlink_dep = NULL;
  
  /* Inicialitzem el buffer circular del teclat */
  keyboard_start = 0;
  keyboard_end = 0;
}

struct logic_device * get_free_device()
{
  int i;
  for (i=0; i<NR_LOGIC_DEVICES; i++) 
    if (devices[i].status == FREE) return &devices[i];
  return NULL;
}

struct logic_device * new_file(const char * path, int flags)
{
  int block;
  struct logic_device * ld;
  
  /* Busquem un slot de logic_device lliure*/
  if ((ld = get_free_device()) == NULL) return NULL;
  
  /* Busquem un bloc de memoria a disc*/
  if ((block = get_free_block()) == -1) return NULL;
  
  FAT[block] = EOF;
  
  ld->status = USED;
  strcopy(ld->name,path);
  ld->acces_mode = flags&0x3;
  //ld->ops.sys_open_dep = sys_open_file;
  ld->ops.sys_write_dep = sys_write_file;
  ld->ops.sys_read_dep = sys_read_file;
  ld->ops.sys_unlink_dep = sys_unlink_file;
  ld->first_block = block;
  
  return ld;
}

struct logic_device * search_logic_device(const char *path)
{
  int i;
  for (i=0; i<NR_LOGIC_DEVICES; i++)
    if (devices[i].status == USED && strcmp(devices[i].name,path)==0) return &(devices[i]);
  return NULL;
}

struct TFA_item * get_TFA_free_entry()
{
  int i;
  for (i = 0; i < TFA_SIZE;++i)
    if (TFA[i].nref == 0) return &(TFA[i]);
  return NULL;
}

int sys_write_console(int fd, char *buffer,int size)
{
  int i;
  for(i=0; i<size; i++) 
    printc(buffer[i]);
  return i;
}

int sys_read_keyboard(int fd, char *buffer,int size)
{
  int i;
  /* si el keyboard_buffer te 'size' caracters disponbles, els copiem i retornem*/
  if (keyboard_count > size)
  {
    for (i=0; i<size; i++) buffer[i] = keyboard_buffer[(keyboard_start++)%KEYBOARD_BUFFER_SIZE];
    keyboard_count -= size;
    return size;
  }
  /* sino, bloquejem el proces fins tenir disponibles el 'size' caracters*/
  else if(current()->Pid > 0)
  {
    current()->read_size = size;
    current()->c_reads = 0;
    current()->read_buffer = buffer;   
    block_task(&keyboardqueue);
  }
  /* en cas del task0, no es pot bloquejar */
  return -1;
}

/* copia del keyboard_buffer al read_buffer els bytes disponibles esperats pel proces*/
void copy_keyboard_buffer(struct task_struct * fbt)
{
  int i, fr, copiats;
  /* Copiem l'espai de pagines de dades de bt a l'espai del proces actual */
  for (i = 0; i < NUM_PAG_DATA; i++)
  {
    fr = fbt->data_frames[i];
    set_ss_pag(PAG_LOG_INIT_DATA_P0+NUM_PAG_DATA+i, fr);
  }
  copiats = keyboard_count < fbt->read_size ? keyboard_count : fbt->read_size;
  /* Accedim al buffer del read de bt i copiem les dades*/
  for (i = 0; i < copiats; i++)
  {
    *(&(fbt->read_buffer[fbt->c_reads++])+(PAGE_SIZE*NUM_PAG_DATA)) = keyboard_buffer[(keyboard_start++)%KEYBOARD_BUFFER_SIZE];
  }
  fbt->read_size -= copiats;
  keyboard_count -= copiats;
  /* Borrem l'espai de pagines de dades de bt de l'espai del proces actual */
  for (i = 0; i < NUM_PAG_DATA; i++)
    del_ss_pag(PAG_LOG_INIT_DATA_P0+NUM_PAG_DATA+i);
}

void afegir_buffer_keyboard(char c)
{
  struct task_struct * fbt; /* primer proces bloquejat */
  /* comprovem si el buffer de teclat no es ple, si es ple es perd el caracter*/
  if (keyboard_count < KEYBOARD_BUFFER_SIZE)
  {
    printk("Afegim lletra al buffer!\n");
    keyboard_buffer[keyboard_end] = c;
    (keyboard_end++)%KEYBOARD_BUFFER_SIZE;
    keyboard_count++;

    /* gestio de procesos bloquejats*/
    if (!(list_empty(&keyboardqueue)))
    {
      fbt = list_head_to_task_struct(list_first(&keyboardqueue));
      /* si hem llegit els N caracters demanats pel primer proces bloquejat, els copiem i desbloquejem el proces*/
      if (keyboard_count == (fbt->read_size-fbt->c_reads))
      {
        copy_keyboard_buffer(fbt);
        list_del(&(fbt->p_list));
        list_add_tail(&(fbt->p_list),&runqueue);
        ((union task_union *)(fbt))->stack[KERNEL_STACK_SIZE-10]=fbt->read_size;
      }
      /* si hem omplert el buffer i encara no hem llegits els N caracters demantas,
	       li enviem el buffer sencer al primer proces bloquejat*/
      else if (keyboard_count == KEYBOARD_BUFFER_SIZE)
      {
        copy_keyboard_buffer(fbt);
      }
    }
  }
}

int sys_write_file(int fd, char *buffer,int size)
{
  int nblock = current()->channels[fd]->seq_pos/BLOCK_SIZE;
  int blockpos = current()->channels[fd]->seq_pos%BLOCK_SIZE;
  int block = current()->channels[fd]->file->first_block;
  int newblock;
  int i;
  int writes = 0;
  for(i=0; i<nblock; i++) block = FAT[block];
  for(i=0; i<size; i++) 
  {  
    HD[block][blockpos] = buffer[i];
    blockpos++;
    current()->channels[fd]->seq_pos++;
    writes++;
    if (blockpos == BLOCK_SIZE)
    {
      blockpos = 0;
      if (FAT[block] == EOF) {
        if ((newblock = get_free_block())==-1) return -ENOSPC;
        FAT[block] = newblock;
        FAT[newblock] = EOF;
        block = newblock;
        }
      }
      else block = FAT[block];
  }
  return writes; 
}

int sys_read_file(int fd,char *buffer,int size)
{
  int nblock = current()->channels[fd]->seq_pos/BLOCK_SIZE;
  int blockpos = current()->channels[fd]->seq_pos%BLOCK_SIZE;
  int block = current()->channels[fd]->file->first_block;
  int i;
  int reads = 0;
  for(i=0; i<nblock; i++) block = FAT[block];
  for(i=0; i<size; i++) 
  {  
    buffer[i] = HD[block][blockpos];
    blockpos++;
    current()->channels[fd]->seq_pos++;
    reads++;
    if (blockpos == BLOCK_SIZE)
    {
      blockpos = 0;
      block = FAT[block];
    }
  }
  return reads;
}

int sys_unlink_file(struct logic_device * file) {
    free_blocks(file->first_block);
    file->status = FREE;
    return 0;
}



